/*
 * @Author: yuki
 * @Date: 2025-11-05 21:00:37
 * @LastEditTime: 2025-11-06 18:20:14
 * @Description: 请求工具
 */
import type { Response } from '@horoscope/shared';
import axios from 'axios';
import type {
  AxiosInstance,
  CancelTokenSource,
  AxiosRequestConfig,
  AxiosError
} from 'axios';
import stringify from 'fast-json-stable-stringify';

import store, { logout, setShowLoginModal } from '@/store';

// 创建 Axios实例
const axiosInstance: AxiosInstance = axios.create({
  timeout: 10000, // 10秒超时
  headers: {
    'Content-Type': 'application/json'
  },
  baseURL: 'http://localhost:9000'
});

// 存储当前 pending 的请求取消令牌
export const pendingRequests = new Map<string, CancelTokenSource>();

/**
 * 过滤掉时间戳参数 _t，并序列化参数对象
 * 使用 fast-json-stable-stringify 确保相同参数生成相同的 key
 */
const serializeParams = (params: unknown): string => {
  if (!params) return '';

  // 如果是对象，过滤掉 _t 参数
  if (typeof params === 'object' && !Array.isArray(params)) {
    const paramObj = params as Record<string, unknown>;
    const filteredParams = Object.keys(paramObj).reduce<
      Record<string, unknown>
    >((acc, key) => {
      if (key !== '_t') {
        acc[key] = paramObj[key];
      }
      return acc;
    }, {});
    return stringify(filteredParams);
  }

  // 其他类型直接序列化
  return stringify(params);
};

/**
 * 生成请求的唯一标识 key
 * 包含请求方法、URL 和参数（GET 的 params，POST 的 data）
 */
export const generateRequestKey = (config: AxiosRequestConfig): string => {
  const method = config.method?.toUpperCase() ?? 'GET';
  const url = config.url ?? '';

  let jsonStr = '';
  if (method === 'GET' || method === 'DELETE') {
    jsonStr = serializeParams(config.params);
  } else {
    jsonStr = serializeParams(config.data);
  }

  return `${method}-${url}${jsonStr ? `-${jsonStr}` : ''}`;
};

// 请求拦截器
axiosInstance.interceptors.request.use(
  config => {
    const token =
      (config.headers.token as string | undefined | null) ??
      window.localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    const requestKey = generateRequestKey(config);
    if (pendingRequests.has(requestKey)) {
      const cancelToken = pendingRequests.get(requestKey);
      cancelToken?.cancel('请求被取消，因为有新的相同请求发起');
      pendingRequests.delete(requestKey);
    }
    const source = axios.CancelToken.source();
    config.cancelToken = source.token;
    pendingRequests.set(requestKey, source);
    return config;
  },
  (error: AxiosError) => Promise.reject(error)
);

// 响应拦截器 - 清除已完成的请求
axiosInstance.interceptors.response.use(
  response => {
    const requestKey = generateRequestKey(response.config);
    pendingRequests.delete(requestKey);
    return response.data;
  },
  (error: AxiosError) => {
    // 非主动取消的请求（如超时、网络错误等），清除 pending 记录
    if (!axios.isCancel(error)) {
      const requestKey = generateRequestKey(error.config as AxiosRequestConfig);
      pendingRequests.delete(requestKey);
    }
    if (error.response?.status === 401) {
      store.dispatch(logout());
      store.dispatch(setShowLoginModal(true));
    }
    return Promise.reject(error);
  }
);

export const GET = <T>(
  url: string,
  params?: Record<string, any>,
  config?: AxiosRequestConfig
): Promise<Response<T>> => {
  const requestConfig = {
    ...config,
    params: {
      ...params,
      _t: Date.now()
    }
  };
  return new Promise<Response<T>>(
    (
      resolve: (value: Response<T>) => void,
      reject: (error: AxiosError) => void
    ) => {
      axiosInstance
        .get<Response<T>>(url, requestConfig)
        .then(res => {
          resolve(res as unknown as Response<T>);
        })
        .catch(reject);
    }
  );
};

export const POST = <T>(
  url: string,
  data?: Record<string, any>,
  config?: AxiosRequestConfig
): Promise<Response<T>> => {
  const requestConfig = {
    ...config,
    data: {
      ...data
    }
  };

  return new Promise<Response<T>>(
    (resolve, reject: (error: AxiosError) => void) => {
      axiosInstance
        .post<Response<T>>(url, data, requestConfig)
        .then(res => {
          resolve(res as unknown as Response<T>);
        })
        .catch(reject);
    }
  );
};

export default axiosInstance;
